From ff612419cd913694ca33a8bb8adc7e6dfe9e7400 Mon Sep 17 00:00:00 2001 From: Manish Singh Date: Sun, 17 Feb 2002 00:07:48 +0000 Subject: [PATCH] Added multiple selection API 2002-02-16 Manish Singh * gtk/gtkfilesel.[ch]: Added multiple selection API --- ChangeLog | 4 + ChangeLog.pre-2-0 | 4 + ChangeLog.pre-2-10 | 4 + ChangeLog.pre-2-2 | 4 + ChangeLog.pre-2-4 | 4 + ChangeLog.pre-2-6 | 4 + ChangeLog.pre-2-8 | 4 + gtk/gtkfilesel.c | 322 ++++++++++++++++++++++++++++++++++++++++----- gtk/gtkfilesel.h | 19 ++- 9 files changed, 323 insertions(+), 46 deletions(-) diff --git a/ChangeLog b/ChangeLog index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 31554a42d5..5bad0b16c9 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,7 @@ +2002-02-16 Manish Singh + + * gtk/gtkfilesel.[ch]: Added multiple selection API + 2002-02-16 Manish Singh * gdk/linux-fb/miarc.c: use fabs() on a double instead of abs() diff --git a/gtk/gtkfilesel.c b/gtk/gtkfilesel.c index 236b015f99..a19d7b9912 100644 --- a/gtk/gtkfilesel.c +++ b/gtk/gtkfilesel.c @@ -396,10 +396,17 @@ static void gtk_file_selection_abort (GtkFileSelection *fs); static void gtk_file_selection_update_history_menu (GtkFileSelection *fs, gchar *current_dir); -static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data); +static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data); static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data); static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data); +static void free_selected_names (GPtrArray *names); + +#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) +#define compare_filenames(a, b) strcmp(a, b) +#else +#define compare_filenames(a, b) g_ascii_strcasecmp(a, b) +#endif static GtkWindowClass *parent_class = NULL; @@ -1116,8 +1123,8 @@ gtk_file_selection_set_filename (GtkFileSelection *filesel, * gtk_file_selection_get_filename: * @filesel: a #GtkFileSelection * - * This function returns the selected filename in the C runtime's - * multibyte string encoding, which may or may not be the same as that + * This function returns the selected filename in encoding of + * g_filename_from_utf8(), which may or may not be the same as that * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8(). * The returned string points to a statically allocated buffer and * should be copied if you plan to keep it around. @@ -1199,7 +1206,19 @@ gtk_file_selection_destroy (GtkObject *object) cmpl_free_state (filesel->cmpl_state); filesel->cmpl_state = NULL; } - + + if (filesel->selected_names) + { + free_selected_names (filesel->selected_names); + filesel->selected_names = NULL; + } + + if (filesel->last_selected) + { + g_free (filesel->last_selected); + filesel->last_selected = NULL; + } + GTK_OBJECT_CLASS (parent_class)->destroy (object); } @@ -1815,7 +1834,8 @@ gtk_file_selection_update_history_menu (GtkFileSelection *fs, } static gchar * -get_real_filename (gchar *filename) +get_real_filename (gchar *filename, + gboolean free_old) { #ifdef G_WITH_CYGWIN /* Check to see if the selection was a drive selector */ @@ -1823,7 +1843,10 @@ get_real_filename (gchar *filename) { /* It is... map it to a CYGWIN32 drive */ gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0])); - g_free(filename); + + if (free_old) + g_free (filename); + return temp_filename; } #else @@ -1844,7 +1867,7 @@ gtk_file_selection_file_activate (GtkTreeView *tree_view, gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1); - filename = get_real_filename (filename); + filename = get_real_filename (filename, TRUE); gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename); gtk_button_clicked (GTK_BUTTON (fs->ok_button)); @@ -1852,27 +1875,6 @@ gtk_file_selection_file_activate (GtkTreeView *tree_view, g_free (filename); } -static void -gtk_file_selection_file_changed (GtkTreeSelection *selection, - gpointer user_data) -{ - GtkFileSelection *fs = GTK_FILE_SELECTION (user_data); - GtkTreeModel *model; - GtkTreeIter iter; - - if (gtk_tree_selection_get_selected (selection, &model, &iter)) - { - gchar *filename; - - gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1); - filename = get_real_filename (filename); - - gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename); - - g_free (filename); - } -} - static void gtk_file_selection_dir_activate (GtkTreeView *tree_view, GtkTreePath *path, @@ -2091,6 +2093,261 @@ gtk_file_selection_abort (GtkFileSelection *fs) gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf); } +/** + * gtk_file_selection_set_select_multiple: + * @filesel: a #GtkFileSelection + * @select_multiple: whether or not the user is allowed to select multiple + * files in the file list. + * + * Sets whether the user is allowed to select multiple files in the file list. + * Use gtk_file_selection_get_selections () to get the list of selected files. + **/ +void +gtk_file_selection_set_select_multiple (GtkFileSelection *filesel, + gboolean select_multiple) +{ + GtkTreeSelection *sel; + GtkSelectionMode mode; + + g_return_if_fail (GTK_IS_FILE_SELECTION (filesel)); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)); + + mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE; + + gtk_tree_selection_set_mode (sel, mode); +} + +/** + * gtk_file_selection_get_select_multiple: + * @filesel: a #GtkFileSelection + * + * Determines whether or not the user is allowed to select multiple files in + * the file list. See gtk_file_selection_set_select_multiple(). + * + * Return value: %TRUE if the user is allowed to select multiple files in the + * file list + **/ +gboolean +gtk_file_selection_get_select_multiple (GtkFileSelection *filesel) +{ + GtkTreeSelection *sel; + + g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE); + + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)); + return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE); +} + +static void +multiple_changed_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GPtrArray *names = data; + gchar *filename; + + gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1); + + g_ptr_array_add (names, filename); +} + +static void +free_selected_names (GPtrArray *names) +{ + gint i; + + for (i = 0; i < names->len; i++) + g_free (g_ptr_array_index (names, i)); + + g_ptr_array_free (names, TRUE); +} + +static void +gtk_file_selection_file_changed (GtkTreeSelection *selection, + gpointer user_data) +{ + GtkFileSelection *fs = GTK_FILE_SELECTION (user_data); + GPtrArray *new_names; + gchar *filename; + const gchar *entry; + gint index = -1; + + new_names = g_ptr_array_sized_new (8); + + gtk_tree_selection_selected_foreach (selection, + multiple_changed_foreach, + new_names); + + /* nothing selected */ + if (new_names->len == 0) + { + g_ptr_array_free (new_names, TRUE); + + if (fs->selected_names != NULL) + { + free_selected_names (fs->selected_names); + fs->selected_names = NULL; + } + + goto maybe_clear_entry; + } + + if (new_names->len != 1) + { + GPtrArray *old_names = fs->selected_names; + + if (old_names != NULL) + { + /* A common case is selecting a range of files from top to bottom, + * so quickly check for that to avoid looping over the entire list + */ + if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1), + g_ptr_array_index (new_names, new_names->len - 1)) != 0) + index = new_names->len - 1; + else + { + gint i = 0, j = 0, cmp; + + /* do a quick diff, stopping at the first file not in the + * old list + */ + while (i < old_names->len && j < new_names->len) + { + cmp = compare_filenames (g_ptr_array_index (old_names, i), + g_ptr_array_index (new_names, j)); + if (cmp < 0) + { + i++; + } + else if (cmp == 0) + { + i++; + j++; + } + else if (cmp > 0) + { + index = j; + break; + } + } + + /* we ran off the end of the old list */ + if (index == -1 && i < new_names->len) + index = j; + } + } + else + { + /* A phantom anchor still exists at the point where the last item + * was selected, which is used for subsequent range selections. + * So search up from there. + */ + if (compare_filenames (fs->last_selected, + g_ptr_array_index (new_names, 0)) == 0) + index = new_names->len - 1; + else + index = 0; + } + } + else + index = 0; + + if (fs->selected_names != NULL) + free_selected_names (fs->selected_names); + + fs->selected_names = new_names; + + if (index != -1) + { + if (fs->last_selected != NULL) + g_free (fs->last_selected); + + fs->last_selected = g_strdup (g_ptr_array_index (new_names, index)); + filename = get_real_filename (fs->last_selected, FALSE); + + gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename); + + if (filename != fs->last_selected) + g_free (filename); + + return; + } + +maybe_clear_entry: + + entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)); + if ((entry != NULL) && (fs->last_selected != NULL) && + (compare_filenames (entry, fs->last_selected) == 0)) + gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), ""); +} + +/** + * gtk_file_selection_get_selections: + * @filesel: a #GtkFileSelection + * + * Retrieves the list of file selections the user has made in the dialog box. + * This function is intended for use when the user can select multiple files + * in the file list. The first file in the list is equivalent to what + * gtk_file_selection_get_filename() would return. + * + * The filenames are in the encoding of g_filename_from_utf8, which may or may + * not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call + * g_filename_to_utf8() on each string. + * + * Return value: a newly-allocated %NULL-terminated array of strings. Use + * g_strfreev() to free it. + **/ +gchar ** +gtk_file_selection_get_selections (GtkFileSelection *filesel) +{ + GPtrArray *names; + gchar **selections; + gchar *filename, *dirname; + gchar *current, *buf; + gint i, count; + + g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL); + + names = filesel->selected_names; + + selections = g_new (gchar *, names->len + 2); + + filename = g_strdup (gtk_file_selection_get_filename (filesel)); + + if (strlen (filename) == 0) + { + g_free (filename); + return NULL; + } + + count = 0; + selections[count++] = filename; + + if (names != NULL) + { + dirname = g_path_get_dirname (filename); + + for (i = 0; i < names->len; i++) + { + buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1, + NULL, NULL, NULL); + current = g_build_filename (dirname, buf, NULL); + g_free (buf); + + if (compare_filenames (current, filename) != 0) + selections[count++] = current; + else + g_free (current); + } + } + + selections[count] = NULL; + + return selections; +} + /**********************************************************************/ /* External Interface */ /**********************************************************************/ @@ -3524,13 +3781,8 @@ static gint compare_cmpl_dir (const void *a, const void *b) { -#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) - return strcmp ((((CompletionDirEntry*)a))->entry_name, - (((CompletionDirEntry*)b))->entry_name); -#else - return g_strcasecmp ((((CompletionDirEntry*)a))->entry_name, - (((CompletionDirEntry*)b))->entry_name); -#endif + return compare_filenames ((((CompletionDirEntry*)a))->entry_name, + (((CompletionDirEntry*)b))->entry_name); } static gint diff --git a/gtk/gtkfilesel.h b/gtk/gtkfilesel.h index 757c957026..3aad672fb1 100644 --- a/gtk/gtkfilesel.h +++ b/gtk/gtkfilesel.h @@ -74,7 +74,9 @@ struct _GtkFileSelection GtkWidget *button_area; GtkWidget *action_area; - + + GPtrArray *selected_names; + gchar *last_selected; }; struct _GtkFileSelectionClass @@ -100,6 +102,11 @@ void gtk_file_selection_complete (GtkFileSelection *filesel, void gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel); void gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel); +gchar** gtk_file_selection_get_selections (GtkFileSelection *filesel); + +void gtk_file_selection_set_select_multiple (GtkFileSelection *filesel, + gboolean select_multiple); +gboolean gtk_file_selection_get_select_multiple (GtkFileSelection *filesel); #ifdef __cplusplus } @@ -107,13 +114,3 @@ void gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel); #endif /* __GTK_FILESEL_H__ */ - - - - - - - - - - -- 2.30.2